﻿using System;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.SharePoint;
using System.Reflection;
using System.Web;
using System.Globalization;
using System.Text;

namespace dpruna.SharePoint
{
    public static class Helpers
    {
        public const string SelectedDataSeparator = ";";

        public static bool IsGuid(string strId)
        {
            if (string.IsNullOrEmpty(strId))
            {
                return false;
            }
            strId = strId.Trim();
            if (strId.Length < 0x20)
            {
                return false;
            }
            if (strId.Contains("x") || strId.Contains("X"))
            {
                strId = strId.Replace(" ", "");
                return Regex.IsMatch(strId, @"^\{0[x|X][a-fA-F\d]{8},(0[x|X][a-fA-F\d]{4},){2}\{(0[x|X][a-fA-F\d]{2},){7}0[x|X][a-fA-F\d]{2}\}\}$", RegexOptions.Compiled);
            }
            return Regex.IsMatch(strId, @"^([a-fA-F\d]{8}-([a-fA-F\d]{4}-){3}[a-fA-F\d]{12}|\([a-fA-F\d]{8}-([a-fA-F\d]{4}-){3}[a-fA-F\d]{12}\)|\{[a-fA-F\d]{8}-([a-fA-F\d]{4}-){3}[a-fA-F\d]{12}\}|[a-fA-F\d]{32})$", RegexOptions.Compiled);
        }
        public static SPList GetListById(SPWeb web, Guid uniqueId, bool throwException)
        {
            SPList list = web.Lists.OfType<SPList>().FirstOrDefault(s => s.ID == uniqueId);
            if (list == null && throwException)
                throw new ArgumentException(String.Format("List with ID {0} not found.", uniqueId));
            return list;
        }
        public static SPField GetFieldByInternalName(this SPFieldCollection fieldCollection, string fieldName, bool throwException)
        {
            SPField field = fieldCollection.OfType<SPField>().FirstOrDefault(s => s.Title.Equals(fieldName) || s.InternalName.Equals(fieldName));
            if (field == null && throwException)
                throw new Exception(String.Format("Field with name {0} not found.", fieldName));
            return field;
        }
        public static SPField GetFieldFromQueryString(HttpRequest request, SPFieldCollection fields)
        {
            Guid guid;
            string convertToGuid = request.QueryString.Get("FieldId");
            string str2 = request.QueryString.Get("Field");
            SPField fieldById = null;
            BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod;
            if (Helpers.TryParseGuid(convertToGuid, out guid))
            {
                fieldById = fields.InvokeMethod<SPField>("GetFieldById", bindingFlags, new object[] { guid, true });
            }
            if ((fieldById == null) && !string.IsNullOrEmpty(str2))
            {
                fieldById = fields.InvokeMethod<SPField>("GetField", bindingFlags, new object[] { str2, false });
            }
            return fieldById;
        }
        public static bool TryParseGuid(string convertToGuid, out Guid guid)
        {
            guid = Guid.Empty;
            if (string.IsNullOrEmpty(convertToGuid) || !IsGuid(convertToGuid))
            {
                return false;
            }
            try
            {
                guid = new Guid(convertToGuid);
            }
            catch (ArgumentNullException)
            {
                return false;
            }
            catch (FormatException)
            {
                return false;
            }
            catch (OverflowException)
            {
                return false;
            }
            return true;
        }
        public static bool StsCompareStrings(string str1, string str2)
        {
            CompareInfo compareInfo = CultureInfo.InvariantCulture.CompareInfo;
            return (0 == compareInfo.Compare(str1, str2, CompareOptions.IgnoreCase));
        }
        public static string SubstituteArgumentValues(string format, params object[] values)
        {
            for (int i = 0; i < values.Length; i++)
            {
                string oldValue = "%" + ((i + 1)).ToString(CultureInfo.InvariantCulture);
                format = format.Replace(oldValue, values[i].ToString());
            }
            return format;
        }
        public static string ToSemicolumnSeparatedLookupIds(this SPFieldLookupValueCollection lkpValueCollection)
        {
            StringBuilder sb = new StringBuilder();
            foreach (SPFieldLookupValue lkpValue in lkpValueCollection)
                sb.AppendFormat("{0}{1}", lkpValue.LookupId, SelectedDataSeparator);

            return sb.ToString().TrimEnd(SelectedDataSeparator.ToCharArray());
        }
        public static string ToSemicolumnSeparatedLookupValues(this SPFieldLookupValueCollection lkpValueCollection)
        {
            StringBuilder sb = new StringBuilder();
            foreach (SPFieldLookupValue lkpValue in lkpValueCollection)
                sb.AppendFormat("{0}{1}", lkpValue.LookupValue, SelectedDataSeparator);

            return sb.ToString().TrimEnd(SelectedDataSeparator.ToCharArray());
        }
        public static string ToSPFieldLookupValueCollectionString(this string stringValue)
        {
            string[] chunks = stringValue.Split(SelectedDataSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            return String.Join(";#;#", chunks);
        }

        #region Generic Extension Methods
        public static T GetPrivatePropertyValue<T>(this object obj, string propName)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");
            PropertyInfo pi = obj.GetType().GetProperty(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            if (pi == null)
                throw new ArgumentOutOfRangeException("propName", string.Format("Property {0} was not found in Type {1}", propName, obj.GetType().FullName));
            return (T)pi.GetValue(obj, null);
        }

        public static T GetPrivateFieldValue<T>(this object obj, string propName)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");
            Type t = obj.GetType();
            FieldInfo fi = null;
            while (fi == null && t != null)
            {
                fi = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                t = t.BaseType;
            }
            if (fi == null)
                throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName));
            return (T)fi.GetValue(obj);
        }

        public static void SetPrivatePropertyValue<T>(this object obj, string propName, T val)
        {
            Type t = obj.GetType();
            if (t.GetProperty(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) == null)
                throw new ArgumentOutOfRangeException("propName", string.Format("Property {0} was not found in Type {1}", propName, obj.GetType().FullName));
            t.InvokeMember(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, obj, new object[] { val });
        }

        public static void SetPrivateFieldValue<T>(this object obj, string propName, T val)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");
            Type t = obj.GetType();
            FieldInfo fi = null;
            while (fi == null && t != null)
            {
                fi = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                t = t.BaseType;
            }
            if (fi == null)
                throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName));
            fi.SetValue(obj, val);
        }

        public static T InvokeMethod<T>(this object obj, string methodName, BindingFlags bindingFlags, object[] methodParams)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");
            Type t = obj.GetType();
            MethodInfo mi = null;
            while(mi == null && t != null)
            {
                mi = t.GetMethod(methodName, bindingFlags);
                t = t.BaseType;
            }
            if (mi == null)
                throw new ArgumentOutOfRangeException("methodName", String.Format("Method {0} was not found in Type {1}", methodName, obj.GetType().FullName));

            return (T)mi.Invoke(obj, methodParams);
        }

        public static void InvokeMethod(this object obj, string methodName, BindingFlags bindingFlags, object[] methodParams)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");
            Type t = obj.GetType();
            MethodInfo mi = null;
            while (mi == null && t != null)
            {
                mi = t.GetMethod(methodName, bindingFlags);
                t = t.BaseType;
            }
            if (mi == null)
                throw new ArgumentOutOfRangeException("methodName", String.Format("Method {0} was not found in Type {1}", methodName, obj.GetType().FullName));

            mi.Invoke(obj, methodParams);
        }
        #endregion
    }
}
